低级输入
使用至今所定义的这个计算器,可以发现一些不方便的地方:需要记住在每个表达式的最后加一个分号,以使它的值能够被打印出来;每个名字都需要用空白结束等。这些都非常令人讨厌。例如,x=7将被作为一个标识符,而不是标识符x后跟运算符=和数7。用读入单个字符的代码来代替get_token()里那种基于类型的默认输入操作,就可以解决这两个问题。
首先,我们可将换行符号看做与分号等价,也当做表达式结束:
Token_value get_token()
{
char ch;
do { // 跳过空白,除了'\n'
if(!cin.get(ch)) return curr_tok = END;
}while(ch!='\n' && isspace(ch));
switch(ch) {
case ';':
case '\n':
return curr_tok = PRINT;
}
这里用了一个do语句,它等价于while语句,除了被控制的语句至少总要执行一次之外。调用cin.get(ch)从标准输入流读一个字符到ch里。按照默认方式,get不会像 >> 运算符那样跳过空白。检测if(!cin.get(ch))在无法从cin读到字符时为真;在这种情况下,就返回END以结束计算器的本次执行。这里用了运算符!(否定),因为get()在成功的情况下将返回true。
标准库函数isspace()提供了对空白的标准检测(20.4.2节)。当c是空白字符时,isspace()比直接检测各个空白字符要快很多。类似的函数包括检测一个字符是否是数字---isdigit(),是否是字母---isalpha(),是否是数字或字母---isalnum()。
在跳过空白之后,下一个字符将用于确定读来的是哪些词法单词。
采用 >> 读入字符串直到遇到空白会引起问题,这一问题可以通过一次读一个字符,直到遇到非字母非数字字符的方式解决:
default: // NAME, NAME=, 或者错误
if(isalpha(ch)) {
string_value = ch;
while(cin.get(ch) && isalnum(ch)) string_value.push_back(ch);
cin.putback(ch);
return curr_tok = NAME;
}
error("bad token");
return curr_tok = PRINT;
幸运是是,这两个改进的实现都只要修改一小段局部代码。构造出这样的程序,使程序的改进能通过局部性修改实现,这也是一个非常重要的设计目标。
🔚